#include <string.h>
#include <stdlib.h>

#include "Xml.hpp"
/**
 * Xml
 **/
static Xml voidXml;
Xml::Xml()
{
	this->name=this->value=NULL;
	this->children=this->next=NULL;
}
Xml::Xml(char* xml)
{
	this->parseXml(this,xml);
}
Xml::Xml(char* name,char* value)
{
	this->name=(char*)malloc(255);
	strcpy(this->name,name);
	this->value=(char*)malloc(255);
	if(value)
		strcpy(this->value,value);
	else
		this->value[0]=0;
	this->children=this->next=NULL;
}
char* Xml::parseXml(Xml* xmlObject,char* xml)
{
	int first=1;
	char close[255];
	while(*xml)
	{
		/* find tag */
		while(*xml && *xml!='<')
			++xml;

		/* unexpected end */
		if(!*xml)
			return NULL;
		
		/* closing tag */
		if(*(xml+1)=='/')
			return xml+1;

		if(!first)
			xmlObject=xmlObject->next=new Xml();
		else
			first=0;

		/* init */
		xmlObject->name=new char[255];
		*xmlObject->name=0;
		xmlObject->value=new char[1024];
		*xmlObject->value=0;
		xmlObject->children=xmlObject->next=NULL;

		/* read tag */
		if(!(xml=xmlObject->readTag(xmlObject->name,xml)))
			return NULL;


		/* auto closing tag ? */
		if(*(xml-2)=='/')
			xmlObject->name[strlen(xmlObject->name)-1]=0;
		else
		{

			/* find value or child */
			while(*xml && (*xml=='\t' || *xml==' ' || *xml=='\n' || *xml=='\r'))
				++xml;

			/* unexpected end */
			if(!*xml)
				return NULL;

			/* value */
			if(*xml!='<')
			{
				if(!(xml=xmlObject->readValue(xmlObject->value,xml)))
					return NULL;
				xmlObject->readTag(close,xml);
				++xml;
			}
			/* child or closing tag */
			else
			{
				/* child */
				if(*++xml && *xml!='/')
				{	
					xmlObject->children=new Xml();

					/* recursive child stuff */
					if(!(xml=xmlObject->parseXml(xmlObject->children,xml-1)))
						return NULL;
				}
			}
		}
	}
	return xml;
}
/**
 * read xml tag
 * @param xml xml char* ("<name>...")
 * @param tag return tag
 * @return incremented xml or NULL if error
 **/
inline char* Xml::readTag(char* tag,char* xml)
{
	int i=0;
	while(*++xml && *xml!='>')
		tag[i++]=*xml;
	tag[i]=0;
	return *xml?xml+1:NULL;
}

inline char* Xml::readValue(char* value,char* xml)
{
	int i=0;
	while(*xml && *xml!='<')
		value[i++]=*xml++;
	/* TODO : trim function */
	value[i]=0;
	return *xml?xml:NULL;
}

char* Xml::writeXml(Xml* xmlObject,int depth)
{
	if(!xmlObject)
		xmlObject=this;
	char*temp,*R=new char[8192];
	int i=0;
	int optimize=0;
	*R=0;
	while(i++<depth)
	{
		strcat(R+optimize,"\t");
		++optimize;
	}
	strcat(R+optimize++,"<");
	strcat(R+optimize,xmlObject->name);
	optimize+=strlen(xmlObject->name);
	if(!*xmlObject->value && !xmlObject->children)
	{
		strcat(R+optimize,"/>\n");
		optimize+=3;
	}
	else
	{
		strcat(R+optimize,">\n");
		optimize+=2;
		if(*xmlObject->value)
		{
			i=0;
			while(i++<depth+1)
				strcat(R+optimize++,"\t");
			strcat(R+optimize,xmlObject->value);
			optimize+=strlen(xmlObject->value);
			strcat(R+optimize++,"\n");
			i=0;
			while(i++<depth)
			{
				strcat(R+optimize,"\t");
				++optimize;
			}
			strcat(R+optimize,"</");
			optimize+=2;
			strcat(R+optimize,xmlObject->name);
			optimize+=strlen(xmlObject->name);
			strcat(R+optimize,">\n");
			optimize+=2;
		}
		else
		{
			Xml* master=xmlObject;
			temp=xmlObject->writeXml(xmlObject->children,depth+1);
			strcat(R,temp);
			delete temp;
			i=0;
			while(i++<depth)
				strcat(R+optimize++,"\t");
			strcat(R+optimize,"</");
			optimize+=2;
			strcat(R+optimize,master->name);
			optimize+=strlen(master->name);
			strcat(R+optimize,">\n");
			optimize+=2;
		}
	}
	if((xmlObject=xmlObject->next))
	{
		temp=xmlObject->writeXml(xmlObject,depth);
		strcat(R+optimize,temp);
		delete temp;
	}
	return R;
}


Xml& Xml::operator[](const char* name)
{
	Xml* R=this;
	while(R)
	{
		if(!R->name)
			return voidXml;
		if(!strcmp(name,R->name))
			return *R;
		R=R->next;
	}
	return voidXml;
}

/**
 * destructor
 */
Xml::~Xml()
{
	// TODO : free/delete
}

void Xml::addNode(char* name,char* value)
{
	if(!this->name)
	{
		this->name=(char*)malloc(255);
		strcpy(this->name,name);
		this->value=(char*)malloc(255);
		if(value)
		{
			if(!*this->value)
				strcpy(this->value,value);
			else
				this->value[0]=0;
		}
	}
	else
	{
		Xml* temp=this;
		while(temp->next)
			temp=temp->next;
		temp->next=new Xml(name,value);
	}
}

void Xml::addChild(char* name,char* value)
{
	if(this->children)
		this->children->addNode(name,value);
	else
		this->children=new Xml(name,value);
}
